using System;
using System.Text;

namespace PDUConverter
{
    /// <summary>
    /// Represents an SMS-SUBMIT PDU, an outgoing short message.
    /// </summary>
    public class SmsSubmitPdu : OutgoingSmsPdu
    {
        private string destAddress;
        private byte destTOA;
        private ValidityPeriod validityPeriod;

        /// <summary>
        /// Initializes a new <see cref="T:GsmComm.PduConverter.SmsSubmitPdu" /> instance using default values.
        /// </summary>
        public SmsSubmitPdu()
        {
            base.messageFlags = new SmsSubmitMessageFlags();
            this.DestinationAddress = string.Empty;
            this.ValidityPeriod = new RelativeValidityPeriod(0xa7);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:GsmComm.PduConverter.SmsSubmitPdu" /> class
        /// using the specified text and destination address.
        /// </summary>
        /// <param name="userDataText">The message text, not exceeding 160 characters.</param>
        /// <param name="destinationAddress">The message's destination address.</param>
        public SmsSubmitPdu(string userDataText, string destinationAddress) : this()
        {
            base.Encode7BitText(userDataText);
            this.DestinationAddress = destinationAddress;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:GsmComm.PduConverter.SmsSubmitPdu" /> class
        /// using the specified PDU string.
        /// </summary>
        /// <param name="pdu">The PDU string to convert.</param>
        /// <param name="includesSmscData">Specifies if the string contains
        /// SMSC data octets at the beginning.</param>
        /// <param name="actualLength">Specifies the actual PDU length, that is the length in bytes without
        /// the SMSC header. Set to -1 if unknown.</param>
        /// <remarks>
        /// <para>This constructor assumes that the string contains an <b>SMS-SUBMIT</b>
        /// PDU data stream as specified
        /// by GSM 07.05.</para>
        /// <para>AbsuluteValidityPeriod and EnhancedValidityPeriod are not
        /// supported and will generate a <see cref="T:System.NotSupportedException" />
        /// when encountered.
        /// </para>
        /// </remarks>
        /// <exception cref="T:System.NotSupportedException">The string contains a
        /// validity and the validity period format is not relative validity.
        /// </exception>
        public SmsSubmitPdu(string pdu, bool includesSmscData, int actualLength)
        {
            if (pdu == string.Empty)
            {
                throw new ArgumentException("pdu must not be an empty string.");
            }
            bool flag = actualLength >= 0;
            int num = actualLength;
            if (!flag || (num > 0))
            {
                int index = 0;
                if (includesSmscData)
                {
                    byte num3 = BcdWorker.GetByte(pdu, index++);
                    if (num3 > 0)
                    {
                        byte num4 = BcdWorker.GetByte(pdu, index++);
                        int length = num3 - 1;
                        string data = BcdWorker.GetBytesString(pdu, index, length);
                        index += length;
                        string address = BcdWorker.DecodeSemiOctets(data);
                        if (address.EndsWith("F") || address.EndsWith("f"))
                        {
                            address = address.Substring(0, address.Length - 1);
                        }
                        base.SetSmscAddress(address, num4);
                    }
                    else
                    {
                        base.SmscAddress = string.Empty;
                    }
                }
                else
                {
                    base.SmscAddress = string.Empty;
                }
                base.messageFlags = new SmsSubmitMessageFlags(BcdWorker.GetByte(pdu, index++));
                if (flag)
                {
                    num--;
                    if (num <= 0)
                    {
                        return;
                    }
                }
                base.MessageReference = BcdWorker.GetByte(pdu, index++);
                byte @byte = BcdWorker.GetByte(pdu, index++);
                byte addressType = BcdWorker.GetByte(pdu, index++);
                if (@byte > 0)
                {
                    int num8 = (((@byte % 2) != 0) ? (@byte + 1) : @byte) / 2;
                    string str3 = BcdWorker.GetBytesString(pdu, index, num8);
                    index += num8;
                    string str4 = BcdWorker.DecodeSemiOctets(str3).Substring(0, @byte);
                    this.SetDestinationAddress(str4, addressType);
                }
                else
                {
                    this.DestinationAddress = string.Empty;
                }
                base.ProtocolID = BcdWorker.GetByte(pdu, index++);
                base.DataCodingScheme = BcdWorker.GetByte(pdu, index++);
                switch (this.MessageFlags.ValidityPeriodFormat)
                {
                    case ValidityPeriodFormat.Unspecified:
                        this.ValidityPeriod = null;
                        break;

                    case ValidityPeriodFormat.Relative:
                        this.ValidityPeriod = new RelativeValidityPeriod(BcdWorker.GetByte(pdu, index++));
                        break;

                    case ValidityPeriodFormat.Absolute:
                        throw new NotSupportedException("Absolute validity period format not supported.");

                    case ValidityPeriodFormat.Enhanced:
                        throw new NotSupportedException("Enhanced validity period format not supported.");

                    default:
                        throw new NotSupportedException("Validity period format \"" + this.MessageFlags.ValidityPeriodFormat.ToString() + "\" not supported.");
                }
                byte dataLength = BcdWorker.GetByte(pdu, index++);
                if (dataLength <= 0)
                {
                    base.SetUserData(null, 0);
                }
                else
                {
                    int num10 = BcdWorker.CountBytes(pdu) - index;
                    string s = BcdWorker.GetBytesString(pdu, index, num10);
                    index += num10;
                    string str6 = string.Empty;
                    for (int i = 0; i < (s.Length / 2); i++)
                    {
                        string byteString = BcdWorker.GetByteString(s, i);
                        if (!Calc.IsHexString(byteString))
                        {
                            break;
                        }
                        str6 = str6 + byteString;
                    }
                    base.SetUserData(Calc.HexToInt(str6), dataLength);
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:GsmComm.PduConverter.SmsSubmitPdu" /> class using the specified text,
        /// destination address and data coding scheme.
        /// </summary>
        /// <param name="userDataText">The message text.</param>
        /// <param name="destinationAddress">The message's destination address.</param>
        /// <param name="dataCodingScheme">Specifies how the userDataText should be encoded.</param>
        /// <remarks><para>The maximum length of the userDataText parameter depends on the alphabet specified with
        /// the dataCodingScheme parameter.</para><para>Common values for the dataCodingScheme are
        /// <see cref="F:GsmComm.PduConverter.DataCodingScheme.NoClass_7Bit" /> for GSM default alphabet and
        /// <see cref="F:GsmComm.PduConverter.DataCodingScheme.NoClass_16Bit" /> for UCS2 alphabet (Unicode).</para></remarks>
        public SmsSubmitPdu(string userDataText, string destinationAddress, byte dataCodingScheme) : this()
        {
            base.DataCodingScheme = dataCodingScheme;
            base.UserDataText = userDataText;
            this.DestinationAddress = destinationAddress;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:GsmComm.PduConverter.SmsSubmitPdu" /> class using the specified text,
        /// destination address and SMSC address.
        /// </summary>
        /// <param name="userDataText">The message text, not exceeding 160 characters.</param>
        /// <param name="destinationAddress">The message's destination address.</param>
        /// <param name="smscAddress">The service center (SMSC) address. Can be an empty string.</param>
        public SmsSubmitPdu(string userDataText, string destinationAddress, string smscAddress) : this()
        {
            base.Encode7BitText(userDataText);
            this.DestinationAddress = destinationAddress;
            base.SmscAddress = smscAddress;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:GsmComm.PduConverter.SmsSubmitPdu" /> class using the specified text,
        /// destination address, SMSC address and data coding scheme.
        /// </summary>
        /// <param name="userDataText">The message text.</param>
        /// <param name="destinationAddress">The message's destination address.</param>
        /// <param name="smscAddress">The service center (SMSC) address. Can be an empty string.</param>
        /// <param name="dataCodingScheme">Specifies how the userDataText should be encoded.</param>
        /// <remarks><para>The maximum length of the userDataText parameter depends on the alphabet specified with
        /// the dataCodingScheme parameter.</para><para>Common values for the dataCodingScheme are
        /// <see cref="F:GsmComm.PduConverter.DataCodingScheme.NoClass_7Bit" /> for GSM default alphabet and
        /// <see cref="F:GsmComm.PduConverter.DataCodingScheme.NoClass_16Bit" /> for UCS2 alphabet (Unicode).</para></remarks>
        public SmsSubmitPdu(string userDataText, string destinationAddress, string smscAddress, byte dataCodingScheme) : this()
        {
            base.DataCodingScheme = dataCodingScheme;
            base.UserDataText = userDataText;
            this.DestinationAddress = destinationAddress;
            base.SmscAddress = smscAddress;
        }

        private string EncodeDestinationAddress(string address, byte addressType)
        {
            if (address.Length == 0)
            {
                return (Calc.IntToHex((byte) 0) + Calc.IntToHex(addressType));
            }
            byte length = (byte) address.Length;
            string str = BcdWorker.EncodeSemiOctets(address);
            return (Calc.IntToHex(length) + Calc.IntToHex(addressType) + str);
        }

        private string EncodeSmscAddress(string address, byte addressType)
        {
            if (address.Length == 0)
            {
                return Calc.IntToHex((byte) 0);
            }
            string str = BcdWorker.EncodeSemiOctets(address);
            byte b = (byte) ((str.Length / 2) + 1);
            return (Calc.IntToHex(b) + Calc.IntToHex(addressType) + str);
        }

        /// <summary>
        /// Returns the relevant timestamp for the message.
        /// </summary>
        /// <returns>Always <see cref="F:GsmComm.PduConverter.SmsTimestamp.None" />. An <see cref="T:GsmComm.PduConverter.SmsSubmitPdu" /> does not have
        /// a timestamp.</returns>
        public override SmsTimestamp GetTimestamp()
        {
            return SmsTimestamp.None;
        }

        /// <summary>
        /// Sets the destination address and type directly without attempting to
        /// autodetect the type.
        /// </summary>
        /// <param name="address">The destination address</param>
        /// <param name="addressType">The address type</param>
        public void SetDestinationAddress(string address, byte addressType)
        {
            this.destAddress = address;
            this.destTOA = addressType;
        }

        /// <summary>
        /// Converts the value of this instance into a string.
        /// </summary>
        /// <param name="excludeSmscData">If true, excludes the SMSC header.</param>
        /// <returns>The encoded string.</returns>
        public override string ToString(bool excludeSmscData)
        {
            string str = string.Empty;
            if (this.MessageFlags.ValidityPeriodFormat == ValidityPeriodFormat.Relative)
            {
                str = Calc.IntToHex((byte) ((RelativeValidityPeriod) this.ValidityPeriod));
            }
            else if (this.MessageFlags.ValidityPeriodFormat != ValidityPeriodFormat.Unspecified)
            {
                throw new NotSupportedException("The specified validity period format \"" + this.MessageFlags.ValidityPeriodFormat.ToString() + "\" is not supported.");
            }
            StringBuilder builder = new StringBuilder();
            if (!excludeSmscData)
            {
                string str2;
                byte num;
                base.GetSmscAddress(out str2, out num);
                builder.Append(this.EncodeSmscAddress(str2, num));
            }
            builder.Append(Calc.IntToHex((byte) base.messageFlags));
            builder.Append(Calc.IntToHex(base.messageReference));
            builder.Append(this.EncodeDestinationAddress(this.destAddress, this.destTOA));
            builder.Append(Calc.IntToHex(base.ProtocolID));
            builder.Append(Calc.IntToHex(base.DataCodingScheme));
            builder.Append(str);
            builder.Append(Calc.IntToHex(base.UserDataLength));
            if (base.UserData != null)
            {
                builder.Append(Calc.IntToHex(base.UserData));
            }
            return builder.ToString();
        }

        /// <summary>
        /// Gets or sets the destination address.
        /// </summary>
        /// <remarks>
        /// <para>When setting the property also the <see cref="P:GsmComm.PduConverter.SmsSubmitPdu.DestinationAddressType" />
        /// property will be set, attempting to autodetect the address type.</para>
        /// <para>When getting the property: The address may be extended with address-type
        /// specific prefixes or other chraracters.</para>
        /// </remarks>
        public string DestinationAddress
        {
            get
            {
                return base.CreateAddressOfType(this.destAddress, this.destTOA);
            }
            set
            {
                byte num;
                string str;
                base.FindTypeOfAddress(value, out num, out str);
                this.destAddress = str;
                this.destTOA = num;
            }
        }

        /// <summary>
        /// Gets the type of the destination address.
        /// </summary>
        /// <remarks>
        /// <para>Represents the Type-of-Address octets for the destination address of the PDU.</para>
        /// </remarks>
        public byte DestinationAddressType
        {
            get
            {
                return this.destTOA;
            }
        }

        /// <summary>
        /// Gets the message flags.
        /// </summary>
        public SmsSubmitMessageFlags MessageFlags
        {
            get
            {
                return (SmsSubmitMessageFlags) base.messageFlags;
            }
        }

        /// <summary>
        /// Gets or sets if the SC should reject duplicate messages.
        /// </summary>
        public bool RejectDuplicates
        {
            get
            {
                return this.MessageFlags.RejectDuplicates;
            }
            set
            {
                this.MessageFlags.RejectDuplicates = value;
            }
        }

        /// <summary>
        /// Gets or sets if a reply path exists.
        /// </summary>
        public bool ReplyPathExists
        {
            get
            {
                return this.MessageFlags.ReplyPathExists;
            }
            set
            {
                this.MessageFlags.ReplyPathExists = value;
            }
        }

        /// <summary>
        /// Gets or sets if s status report should be requested.
        /// </summary>
        public bool RequestStatusReport
        {
            get
            {
                return this.MessageFlags.RequestStatusReport;
            }
            set
            {
                this.MessageFlags.RequestStatusReport = value;
            }
        }

        /// <summary>
        /// Gets or sets if a user data header is present.
        /// </summary>
        public bool UserDataHeaderPresent
        {
            get
            {
                return this.MessageFlags.UserDataHeaderPresent;
            }
            set
            {
                this.MessageFlags.UserDataHeaderPresent = value;
            }
        }

        /// <summary>
        /// Gets or sets the validity period.
        /// </summary>
        /// <remarks>
        /// <para>Represents the TP-Validity-Period octet of the PDU.</para>
        /// <para>The validity period specifies the time when SM expires. If SM is't delivered
        /// before that moment, it is discarded by SC. Validity-Period can be in
        /// three different formats: Relative, Enhanced and Absolute.</para>
        /// </remarks>
        public ValidityPeriod ValidityPeriod
        {
            get
            {
                return this.validityPeriod;
            }
            set
            {
                if (value is RelativeValidityPeriod)
                {
                    this.MessageFlags.ValidityPeriodFormat = ValidityPeriodFormat.Relative;
                }
                else
                {
                    if (value != null)
                    {
                        throw new ArgumentException("Unknown or unsupported validity period format: " + value.GetType().ToString());
                    }
                    this.MessageFlags.ValidityPeriodFormat = ValidityPeriodFormat.Unspecified;
                }
                this.validityPeriod = value;
            }
        }
    }
}